import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import prisma from '@/lib/prisma'
import {
  executeCodeInPiston, // Piston
  mapPistonResultToSimplified, // Piston
  PistonExecuteRequest, // Piston
  MappedPistonResult, // Piston
} from '@/lib/pistonService' // Piston service
import { TestCase, ExperimentStage, Prisma, StageSubmission, Submission } from '@prisma/client' // Added Prisma for types

// 学生提交接口定义（仅用于文档）
interface StudentSubmission {
  id: string
  status: string
  score: number | null
  submittedAt: string | null
  student: {
    id: string
    name: string
    email: string
  }
  stageSubmissions: Array<{
    id: string
    score: number | null
    submittedAt: string | null
    stage: {
      id: string
      title: string
      order: number
    }
  }>
}

// 统计信息接口定义（仅用于文档）
interface SubmissionStats {
  totalSubmissions: number
  totalStudents: number
  submissionRate: number
  statusCounts: {
    IN_PROGRESS: number
    SUBMITTED: number
    GRADED: number
  }
  avgScore: number | null
}

interface StageContentForGrading {
  language?: string;
  languageVersion?: string;
  testCases?: Array<TestCase & { input?: string, expectedOutput?: string, points: number }>;
  timeLimitMs?: number;
  memoryLimitKb?: number;
  compile_timeout?: number; // in ms for Piston
  run_timeout?: number;     // in ms for Piston
  compile_memory_limit?: number; // in bytes for Piston
  run_memory_limit?: number;     // in bytes for Piston
  correctAnswerId?: string; // For MCQs
  points?: number; // For various types
  standardAnswers?: string[]; // For Fill_IN_BLANK
  matchCase?: boolean; // For Fill_IN_BLANK
  [key: string]: any;
}

interface DraftStageAnswer {
  answer: any
  fileName?: string
  fileUrl?: string
  lastSaved: string
}

export async function POST(
  req: NextRequest,
  { params }: { params: { id: string } }
) {
  const session = await getServerSession(authOptions);

  if (!session?.user?.id || session.user.role !== 'STUDENT') {
    return NextResponse.json({ error: 'Forbidden. Only students can submit experiments.' }, { status: 403 });
  }

  const { id: experimentId } = params;
  const studentId = session.user.id;

  if (!experimentId) {
    return NextResponse.json({ error: 'Experiment ID is required' }, { status: 400 });
  }

  try {
    const experiment = await prisma.experiment.findUnique({
      where: { id: experimentId },
      select: { 
        endTime: true, 
        allowLateSubmission: true, 
        // Select all fields from ExperimentStage for robust content parsing
        stages: { 
            select: { 
                id: true, 
                type: true, 
                content: true, 
                order: true, 
                title: true, 
                experimentId: true, 
                allowedLanguages: true, 
                timeLimitMs: true,
                memoryLimitKb: true,
            }
        }
      }
    });

    if (!experiment) {
      return NextResponse.json({ error: 'Experiment not found.' }, { status: 404 });
    }
    
    const now = new Date();
    if (experiment.endTime && now > new Date(new Date(experiment.endTime).getTime() + 60000) && !experiment.allowLateSubmission) { // Added 1 min buffer
        return NextResponse.json({ error: 'Experiment has ended and late submissions are not allowed.' }, { status: 403 });
    }

    const draftExperimentSubmission = await prisma.experimentSubmission.findFirst({
      where: { studentId: studentId, experimentId: experimentId },
      select: { id: true, draftStageAnswers: true }
    });

    if (!draftExperimentSubmission || !draftExperimentSubmission.draftStageAnswers) {
      return NextResponse.json({ error: 'No draft answers found to submit. Please save your progress in stages first.' }, { status: 400 });
    }
    
    let draftStageAnswersObject: Record<string, { answer: any, fileName?: string, fileUrl?: string, lastSaved: string }> = {};
    try {
        if (draftExperimentSubmission.draftStageAnswers) {
            draftStageAnswersObject = JSON.parse(draftExperimentSubmission.draftStageAnswers);
        }
    } catch (parseError) {
        console.error("Failed to parse draftStageAnswers from ExperimentSubmission, submitting empty answers:", parseError);
    }

    const newFormalSubmissionWithAutoGrading = await prisma.$transaction(async (tx) => {
      const createdSubmission = await tx.submission.create({
      data: {
          experimentId: experimentId,
          userId: studentId,
        status: 'SUBMITTED',
          submittedAt: now,
          score: 0, 
      },
    });

      const stageAnswersSnapshotData: Prisma.StageSubmissionCreateManyInput[] = [];
      for (const stage of experiment.stages) {
        const draftAnswerData = draftStageAnswersObject[stage.id];
        let studentAnswerForSnapshot = {}; 
        if (draftAnswerData && draftAnswerData.answer) {
          studentAnswerForSnapshot = draftAnswerData.answer;
        }
        stageAnswersSnapshotData.push({
          submissionId: createdSubmission.id,
          stageId: stage.id,
          answer: JSON.stringify(studentAnswerForSnapshot), 
          submittedAt: now,
          score: null, 
          feedback: null, 
        });
      }

      if (stageAnswersSnapshotData.length > 0) {
        await tx.stageSubmission.createMany({
          data: stageAnswersSnapshotData,
        });
      }

      const createdStageSubmissions = await tx.stageSubmission.findMany({
        where: { submissionId: createdSubmission.id },
        // Include the stage data again from the transaction context if needed, 
        // or rely on `experiment.stages` if it's guaranteed to match by ID.
        // For safety, re-fetching or ensuring `experiment.stages` is passed correctly is good.
        include: { stage: true }, 
      });

      let totalCalculatedScore = 0;
      let overallSubmissionStatus: 'SUBMITTED' | 'GRADED' | 'PARTIALLY_GRADED' = 'SUBMITTED';
      let requiresManualGrading = false;

      for (const ss of createdStageSubmissions) {
        const stageTyped = experiment.stages.find(s => s.id === ss.stageId);
        if (!stageTyped) {
            console.error(`Consistency error: StageSubmission ${ss.id} refers to a stage ${ss.stageId} not found in the experiment's loaded stages.`);
            continue;
        }

        const stageContent: StageContentForGrading = typeof stageTyped.content === 'string' 
                              ? JSON.parse(stageTyped.content) 
                              : stageTyped.content;
        const studentAnswer = typeof ss.answer === 'string' 
                              ? JSON.parse(ss.answer) 
                              : ss.answer;
        let stageScore = 0;
        let stageFeedback: string | null = null;
        let stageManuallyGraded = false;

        if (stageTyped.type === 'MULTIPLE_CHOICE' && stageContent?.correctAnswerId && stageContent.points !== undefined) {
          if (studentAnswer?.selectedOption === stageContent.correctAnswerId) {
            stageScore = stageContent.points;
          }
          await tx.stageSubmission.update({
            where: { id: ss.id },
            data: { score: stageScore, feedback: stageScore === stageContent.points ? 'Correct' : 'Incorrect' },
          });
        } else if (stageTyped.type === 'FILL_IN_BLANK' && stageContent?.standardAnswers && stageContent.points !== undefined) {
          const standardAnswers = stageContent.standardAnswers || [];
          const matchCase = stageContent.matchCase || false;
          const studentInput = studentAnswer?.textInput as string || "";
          let isCorrect = false;
          for (const sa of standardAnswers) {
            if (matchCase ? (studentInput === sa) : (studentInput.toLowerCase() === sa.toLowerCase())) {
              isCorrect = true; break;
            }
          }
          if (isCorrect) {
            stageScore = stageContent.points;
          }
          await tx.stageSubmission.update({
            where: { id: ss.id },
            data: { score: stageScore, feedback: isCorrect ? 'Correct' : 'Incorrect' },
          });
        } else if (stageTyped.type === 'CODE') {
          const studentCodeAnswer = studentAnswer?.code as string | undefined;
          // Prefer language/version from student's answer (if provided, e.g., from instant eval UI choice)
          // Fallback to stage configuration
          const pistonLanguage = studentAnswer?.language || stageContent?.language;
          const pistonVersion = studentAnswer?.languageVersion || stageContent?.languageVersion;

          if (!studentCodeAnswer) {
            stageFeedback = '未提交代码。';
            stageScore = 0;
            await tx.stageSubmission.update({
              where: { id: ss.id },
              data: { score: stageScore, feedback: stageFeedback },
            });
          } else if (!pistonLanguage || !pistonVersion) {
            stageFeedback = `系统配置错误：代码阶段 (${stageTyped.title}) 未正确配置语言或版本。请联系教师。语言: ${pistonLanguage}, 版本: ${pistonVersion}`;
            console.error(stageFeedback + ` Stage ID: ${stageTyped.id}, Experiment ID: ${experimentId}`);
            stageScore = 0;
            await tx.stageSubmission.update({
              where: { id: ss.id },
              data: { score: stageScore, feedback: stageFeedback },
            });
          } else {
            const fileName = `main.${pistonLanguage.split(/[^a-zA-Z0-9]/)[0].toLowerCase() || 'txt'}`;
            // Ensure testCases are properly typed and defaults to an empty array if not present
            const testCasesFromDB: Array<TestCase & { input?: string, expectedOutput?: string, points: number }> = stageContent?.testCases || [];
            
            let accumulatedStageScore = 0;
            let allTestCasesPassed = true;
            const testCaseResultsToCreate: Prisma.TestCaseResultCreateManyInput[] = [];
            let stageProcessingError = false; // Flag for errors during TC processing

            if (testCasesFromDB.length === 0) {
              stageFeedback = '此代码阶段未配置测试用例。需要手动评分。';
              requiresManualGrading = true;
              // Score remains 0 or null as per initial setup for the stage submission
            } else {
              // 首先获取该阶段的所有测试用例
              const testCases = await tx.testCase.findMany({
                where: { stageId: stageTyped.id }
              });

              for (const tc of testCasesFromDB) {
                // 在数据库中查找对应的测试用例
                const dbTestCase = testCases.find(t => t.name === tc.name);
                if (!dbTestCase) {
                  console.warn(`正式提交评测：跳过测试用例 (名称: ${tc.name})，因为在数据库中找不到对应的记录。Stage ID: ${ss.stageId}`);
                  testCaseResultsToCreate.push({
                    stageSubmissionId: ss.id,
                    testCaseId: `missing-id-${tc.name}-${Date.now()}`, // 使用一个唯一的占位符
                    status: 'INTERNAL_ERROR',
                    isCorrect: false,
                    pointsAwarded: 0,
                    actualOutput: null,
                    runtimeError: '测试用例在数据库中不存在，无法关联结果。'
                  });
                  allTestCasesPassed = false;
                  stageProcessingError = true;
                  continue;
                }

                const pistonPayload: PistonExecuteRequest = {
                  language: pistonLanguage,
                  version: pistonVersion,
                  files: [{ name: fileName, content: studentCodeAnswer }],
                  stdin: tc.input || '',
                  args: [], // Default to no args
                  compile_timeout: stageContent.compile_timeout || stageTyped.timeLimitMs || 10000, // Piston expects ms
                  run_timeout: stageContent.run_timeout || stageTyped.timeLimitMs || 3000, // Piston expects ms
                  compile_memory_limit: stageContent.compile_memory_limit || (stageTyped.memoryLimitKb ? stageTyped.memoryLimitKb * 1024 : -1), // Piston expects bytes, -1 for no limit
                  run_memory_limit: stageContent.run_memory_limit || (stageTyped.memoryLimitKb ? stageTyped.memoryLimitKb * 1024 : -1), // Piston expects bytes, -1 for no limit
                };

                let mappedPistonResult: MappedPistonResult;
                try {
                  console.log(`Piston (Formal Submit): 为阶段 ${ss.stageId} 测试用例 ${tc.name} (TC_DB_ID: ${dbTestCase.id}) 提交`);
                  const pistonResponse = await executeCodeInPiston(pistonPayload);
                  mappedPistonResult = mapPistonResultToSimplified(pistonResponse, tc.expectedOutput, tc.points || 0);
                  
                  if (!mappedPistonResult.isCorrect) allTestCasesPassed = false;
                  accumulatedStageScore += mappedPistonResult.pointsAwarded;

                } catch (pistonError: any) {
                  console.error(`Piston (Formal Submit) 评测失败 for TC ${tc.name} (TC_DB_ID: ${dbTestCase.id}):`, pistonError);
                  let errorMsg = pistonError.message || 'Piston 服务通信失败';
                   if (pistonError.message && (pistonError.message.toLowerCase().includes('unknown language') || pistonError.message.toLowerCase().includes('unknown version'))){
                     errorMsg = `Piston 错误: 未知的语言 (${pistonLanguage}) 或版本 (${pistonVersion}). 请检查实验配置。`;
                   } else if (pistonError.message && pistonError.message.toLowerCase().includes('rate limit')){
                     errorMsg = 'Piston 服务请求过于频繁，请稍后重试。'; // More generic for formal submission context
                   }
                  mappedPistonResult = {
                    status: 'INTERNAL_ERROR',
                    isCorrect: false,
                    pointsAwarded: 0,
                    actualOutput: null,
                    runtimeError: errorMsg,
                  };
                  allTestCasesPassed = false;
                  stageProcessingError = true; // Mark that an error occurred
                }
                
                testCaseResultsToCreate.push({
                  stageSubmissionId: ss.id,
                  testCaseId: dbTestCase.id, // 使用数据库中的测试用例 ID
                  status: mappedPistonResult.status.toString(), 
                  isCorrect: mappedPistonResult.isCorrect,
                  pointsAwarded: mappedPistonResult.pointsAwarded,
                  actualOutput: mappedPistonResult.actualOutput,
                  runtimeError: mappedPistonResult.runtimeError,
                });

                // Optional delay between Piston calls, even for formal submission, to be courteous to public APIs
                if (testCasesFromDB.indexOf(tc) < testCasesFromDB.length - 1) {
                    await new Promise(resolve => setTimeout(resolve, 200)); 
                }
              } // End of for loop for test cases
              
              if (testCaseResultsToCreate.length > 0) {
                // 逐个创建测试用例结果，而不是使用 createMany
                for (const result of testCaseResultsToCreate) {
                  try {
                    await tx.testCaseResult.create({
                      data: result
                    });
                  } catch (error) {
                    console.error(`创建测试用例结果失败:`, error);
                    console.error(`失败的记录数据:`, result);
                    // 继续处理其他记录，而不是完全失败
                  }
                }
              }
              stageScore = accumulatedStageScore;
              if (stageProcessingError) {
                stageFeedback = '评测过程中发生一个或多个错误。部分测试可能未成功执行或记录。请检查测试结果详情。';
              } else {
                stageFeedback = allTestCasesPassed ? '所有测试用例通过。' : '部分测试用例未通过或执行时出错。查看详情。';
              }
            } // End of else (testCasesFromDB.length > 0)
            await tx.stageSubmission.update({
              where: { id: ss.id },
              data: { score: stageScore, feedback: stageFeedback },
            });
          } // End of else (valid language and code)
        } else if (stageTyped.type === 'SHORT_ANSWER' || stageTyped.type === 'FILE_UPLOAD') {
            requiresManualGrading = true; // These types always require manual grading
            stageManuallyGraded = true;
            stageFeedback = '待教师评阅';
             await tx.stageSubmission.update({
                where: { id: ss.id },
                data: { feedback: stageFeedback }, // Score remains null until graded
            });
        } else {
            // Unknown type or type that is not auto-gradable and not explicitly manual
            console.warn(`Stage ${ss.stageId} has type ${stageTyped.type} which is not explicitly handled for auto-grading or manual grading designation.`);
            requiresManualGrading = true;
            stageManuallyGraded = true;
            stageFeedback = '此题型待配置评分逻辑或需手动评阅';
             await tx.stageSubmission.update({
                where: { id: ss.id },
                data: { feedback: stageFeedback }, 
            });
        }

        if (stageScore !== null && !stageManuallyGraded) {
            totalCalculatedScore += stageScore;
        }
      } // End of for loop for stage submissions

      // Determine overall submission status
      if (requiresManualGrading) {
        overallSubmissionStatus = 'PARTIALLY_GRADED'; // If any stage needs manual, it's at best partially graded
      } else {
        overallSubmissionStatus = 'GRADED'; // If no manual grading needed, it's fully auto-graded
      }

      await tx.submission.update({
        where: { id: createdSubmission.id },
        data: { 
            score: totalCalculatedScore,
            status: overallSubmissionStatus,
        },
      });

      return { ...createdSubmission, score: totalCalculatedScore, status: overallSubmissionStatus };

    }); // End of transaction

    // Clean draft after successful submission
    await prisma.experimentSubmission.update({
        where: { id: draftExperimentSubmission.id },
        data: { draftStageAnswers: JSON.stringify({}) }
    });

    return NextResponse.json({
      message: 'Experiment submitted successfully and auto-grading completed.',
      submissionId: newFormalSubmissionWithAutoGrading.id,
      status: newFormalSubmissionWithAutoGrading.status,
      score: newFormalSubmissionWithAutoGrading.score,
    });

  } catch (error: any) {
    console.error('Error submitting experiment:', error);
    // Check for Prisma unique constraint error if a student tries to submit twice rapidly
    // Though the model of one formal submission per (student, experiment) is enforced by a Submission record.
    // This route creates a *new* Submission each time it is called.
    // The current logic implies multiple formal submissions are allowed and stored.
    if (error.code === 'P2002') { // Prisma unique constraint violation
        return NextResponse.json({ error: 'Submission failed due to a conflict. This might be a duplicate submission attempt.' , details: error.message}, { status: 409 });
    }
    return NextResponse.json({ error: 'Internal server error while submitting experiment' , details: error.message}, { status: 500 });
  }
}


// GET /api/experiments/[id]/submit
// Fetches all *formal* submissions for a given experiment (for TA/Teacher view) or student's own submissions.
export async function GET(
  req: NextRequest,
  { params }: { params: { id: string } }
) {
  const session = await getServerSession(authOptions);
  if (!session?.user?.id) {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
  }

  const { id: experimentId } = params;
  const studentId = session.user.id;
  const userRole = session.user.role;

  if (!experimentId) {
    return NextResponse.json({ error: 'Experiment ID is required' }, { status: 400 });
  }

  try {
    if (userRole === 'STUDENT') {
      const submissions = await prisma.submission.findMany({
        where: { experimentId: experimentId, userId: studentId },
        include: {
          user: { select: { id: true, name: true, email: true } },
          stageSubmissions: {
            include: {
              stage: { select: { id: true, title: true, order: true } },
              testCaseResults: true, // Include test case results for student
            },
            orderBy: { stage: { order: 'asc' } }
          },
        },
        orderBy: { submittedAt: 'desc' },
      });
      return NextResponse.json(submissions);
    } else if (userRole === 'TEACHER' || userRole === 'ADMIN') {
      // Teachers/Admins see all submissions for the experiment
      const submissions = await prisma.submission.findMany({
        where: { experimentId: experimentId },
        include: {
          user: { select: { id: true, name: true, email: true } },
          stageSubmissions: true,
        },
        orderBy: { submittedAt: 'desc' },
      });

      // 授权学生总数
      const totalAuthorizedStudents = await prisma.experimentAuthorization.count({
        where: { experimentId }
      });

      // 提交过的学生数
      const studentsWhoSubmittedAtLeastOnce = new Set(submissions.map((s: Submission) => s.userId)).size;

      // 总正式提交数
      const totalFormalSubmissions = submissions.length;

      // 状态统计
      const statusCounts = submissions.reduce((acc, s) => {
        acc[s.status as keyof typeof acc] = (acc[s.status as keyof typeof acc] || 0) + 1;
        return acc;
      }, { SUBMITTED: 0, GRADED: 0, PARTIALLY_GRADED: 0 });

      // 每个学生的最高分
      const studentMaxScores: Record<string, number> = {};
      for (const s of submissions) {
        if (s.score !== null) {
          if (!studentMaxScores[s.userId] || s.score > studentMaxScores[s.userId]) {
            studentMaxScores[s.userId] = s.score;
          }
        }
      }
      const maxScores = Object.values(studentMaxScores);
      const avgScoreOfHighestSubmissions = maxScores.length > 0
        ? maxScores.reduce((a, b) => a + b, 0) / maxScores.length
        : null;

      return NextResponse.json({
        submissions,
        stats: {
          studentsWhoSubmittedAtLeastOnce,
          totalAuthorizedStudents,
          totalFormalSubmissions,
          statusCounts,
          avgScoreOfHighestSubmissions,
        },
      });
    } else {
      return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
    }
  } catch (error: any) {
    console.error('Error fetching submissions:', error);
    return NextResponse.json({ error: 'Internal server error' , details: error.message}, { status: 500 });
  }
}